package org.dromara.common.mybatis.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.IService;
import io.reactivex.rxjava3.functions.BiConsumer;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StreamUtils;

import java.util.List;
import java.util.function.Function;

/**
 * 两表关联查询工具类
 *
 * @author zlj
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TwoQueryUtils {

    /**
     * 将根据主表的某个字段查询出的结果集，并设置到主表中
     * list根据sourceField字段关联targetField字段, 通过service查询到结果, 并将结果中的resultField字段赋值到list中每个元素的setField字段
     *
     * @param list        主表信息
     * @param sourceField 主表字段
     * @param targetField 目标结果字段
     * @param service     目标结果查询的Service
     * @param resultField 需要的结果集字段
     * @param setField    将结果集设置到的字段
     * @param <T>         主表类型
     * @param <V>         副表类型
     * @return 设置好的主表信息(不获取返回值也可以, 已经将结果设置进去了)
     */
    public static <T, V, R> List<T> setField(List<T> list, Function<T, Long> sourceField, Function<V, Long> targetField, IService<V> service, Function<V, R> resultField, BiConsumer<T, R> setField) {
        if (CollectionUtil.isNotEmpty(list)) {
            List<Long> ids = StreamUtils.toList(list, sourceField);
            List<V> vList = service.listByIds(ids);
            for (T one : list) {
                V result = vList.stream()
                    .filter(t -> ObjectUtil.equal(sourceField.apply(one), targetField.apply(t)))
                    .findFirst().orElse(null);
                try {
                    setField.accept(one, resultField.apply(result));
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return list;
    }

    /**
     * 将根据主表的某个字段查询出的结果集，并设置到主表中
     *
     * @param list        主表信息
     * @param sourceField 主表字段
     * @param targetField 目标结果字段
     * @param service     目标结果查询的Service
     * @param setField    将结果集设置到的字段
     * @param <T>         主表类型
     * @param <V>         副表类型
     * @return 设置好的主表信息(不获取返回值也可以, 已经将结果设置进去了)
     */
    public static <T, V> List<T> setField(List<T> list, Function<T, Long> sourceField, Function<V, Long> targetField, IService<V> service, BiConsumer<T, V> setField) {
        return TwoQueryUtils.setField(list, sourceField, targetField, service, Function.identity(), setField);
    }

    public static <T, V> T setField(T obj, Function<T, Long> sourceField, IService<V> service, BiConsumer<T, V> setField) {
        Long id = sourceField.apply(obj);
        V v = service.getById(id);
        try {
            setField.accept(obj, v);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return obj;
    }
}
